
/**
 * ***************** ZVue类 *******************
     知识点：
     1.数据劫持 Object.defineProperty(目标对象，属性key, 特性:Object);

     特性
         value: 属性值
         writable: 对象属性值是否可被修改 [true | false]
         configurable: 对象属性是否可被删除 [true | false]
         enumerable: 对象属性是否可被枚举 [true | false]
         get: 给一个属性提供getter方法，当访问这个对象的属性值得时候触发该方法
         det: 给一个属性提供setter方法，当设置属性值得时候触发该方法
 */

/**
 * 1. 对传入的data做响应式处理
 * */

//日志输出
let log = (...args) => console.log(...args);


class ZVue {
    constructor(options){
        if(!options || typeof options !== 'object'){
            return console.log('options is not Object');
        }
        // 保存选项
        this.$options = options;
        this.$data = options.data || {};

        //传入的data做响应式处理, 对$data进行观察
        this.observe(this.$data)


        new Compile(options.el, this)

    }


    /**
     * 观察者模式
     * */
    observe(data){
        //参数必须是对象
        if(!data || typeof data !== 'object')return;

        Object.keys(data).forEach(key => {
            //执行相应化处理
            this.defineReactive(data, key, data[key]);

            //对data执行代理操作，【注意】: NB的操作来了
            this.proxyData(key)
        })
    }

    /**
     * data代理操作
     * */
    proxyData(key){
        let _this = this;
        Object.defineProperty(_this, key, {
            get(){
                return _this.$data[key]
            },
            set(newValue){
                if(_this.$data[key] === newValue)return;
                _this.$data[key] = newValue;
            }
        })

    }

    /**
     * 响应化处理
     * */
    defineReactive(obj, key, value){
        let _this = this;
        //如果data的值是对象继续观
        this.observe(value);

        //创建一个dep，他和key是 1:1的对应关系
        const dep = new Dep();

        Object.defineProperty(obj, key, {
            get(){
                //依赖收集
                Dep.target && dep.addDep(Dep.target);
                return value
            },
            set(newValue){
                if(value === newValue)return;

               //value 闭包局部变量，所以vue在卸载组件会用到$destroy方法
                value = newValue;

                //如果newValue又是一个对象，那么需要在进行观察
                _this.observe(newValue);

                //通知更新
                dep.notify();

               log(value, ':数据更新了，哈哈哈...')

            }
        })
    }
}



/**
 * Dep: 管理若干Watcher实例,通知更新
 * */

class Dep {
    constructor(){
        this.deps = [];
    }

    addDep(dep){
        this.deps.push(dep)
    }

    notify(){
        //执行更新
        this.deps.forEach(dep => dep.update())
    }


}


/***
 * Watcher: 执行具体的更新操作
 * */

class Watcher {
    constructor(vm, key, updater){
        this.vm = vm;
        this.key = key;
        this.updater = updater;

        //避免重复多次加入到dep中
        Dep.target = this;// 用于依赖收集
        this.vm[this.key];
        Dep.target = null;
    }

    update(){
        this.updater.call(this.vm, this.vm[this.key])
    }
}










































